package tagdb.algorithm.match;

import com.compomics.util.experiment.identification.SequenceFactory;
import tagdb.fragmentation.FragmentIon;
import tagdb.fragmentation.Fragmentizer;
import tagdb.fragmentation.SpectrumPeak;
import tagdb.io.MascotGenericFile;
import tagdb.model.Peptide;
import tagdb.model.SuspectHit;
import tagdb.model.params.SearchParams;

import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;

/**
 * <b>PeptideMatcher</b>
 * <p>
 * This class is the start part of the database search java.tagdb.algorithm.
 * It matches the theoretical fragment ions against the experimental spectrum peaks.
 * Additionally, it calculates an intensity-based score and rMIC, i.e. the relative matched ion count (matched fragment ion intensities to TIC in spectrum).
 * </p>
 *
 * @author T.Muth
 */
public class PeptideMatcher {

    /**
     * Holds a peptide object.
     */
    private Peptide peptide;

    /**
     * Holds a spectrum.
     */
    private MascotGenericFile spectrum;

    /**
     * The tag database search parameters.
     */
    private SearchParams params;

    /**
     * The flag for decoy or not.
     */
    private boolean decoy;

    private String description;

    /**
     * Constructs the PeptideMatcher object for peptide and spectrum.
     *
     * @param hit  The suspect hit.
     * @param spectrum The spectrum.
     * @param params   The tag database search parameters.
     */
    public PeptideMatcher(SuspectHit hit, MascotGenericFile spectrum, SearchParams params) throws IOException, InterruptedException {
        this.peptide = hit.getPeptide();
        this.description = SequenceFactory.getInstance().getHeader(hit.getProtein().getAccession()).getDescription();
        this.spectrum = spectrum;
        this.params = params;
        this.decoy = hit.isDecoy();
    }

    /**
     * This method tries to find a match between a peptide sequence and a spectrum.
     * It eventually returns a PSM object with number of matched peaks, score and rMIC.
     *
     * @return The derived PSM
     */
    public PeptideSpectrumMatch findMatch() {

        PeptideSpectrumMatch psm = null;
        Fragmentizer fragmentizer = new Fragmentizer(peptide);
        Map<Double, Double> expPeaks = spectrum.getPeaks();
        Set<Entry<Double, Double>> set = expPeaks.entrySet();
        List<SpectrumPeak> peakList = new ArrayList<SpectrumPeak>();
        double tic = 0.0;
        for (Entry<Double, Double> e : set) {
            peakList.add(new SpectrumPeak(e.getKey(), e.getValue()));
            tic += e.getValue();
        }

        // Get the matched fragment ions
        List<FragmentIon> matchedIons = fragmentizer.getMatchedIons(peakList, params.getFragmentTol());
        double score = 0.0, rMIC = 0.0;
        Map<Double, Double> map = new HashMap<Double, Double>();

        // TODO: Improve the scoring function!
        for (FragmentIon ion : matchedIons) {
            // Avoid multiple matched ion peaks
            if (!map.containsKey(ion.getMZ())) {
                score += ion.getScore();
                rMIC += ion.getIntensity();
                map.put(ion.getMZ(), rMIC);
            }
        }
        // Get the intensity score
        score = score / peptide.getSequence().length();

        // Calculate the relative matched ion count.
        rMIC = rMIC / tic;

        // Construct the PeptideSpectrumMatch object.
        psm = new PeptideSpectrumMatch(peptide.getCharge(), matchedIons.size(), peakList.size(), score, rMIC, decoy);
        psm.setDescription(description);
        psm.setSpectrumTitle(spectrum.getTitle().trim());
        return psm;
    }
}
